package org.apache.commons.vfs.test;
 
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.IsNull.notNullValue;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileType;
import org.apache.commons.vfs.impl.DefaultFileSystemManager;
import org.hamcrest.core.Is;
import org.hamcrest.core.IsEqual;
import org.hamcrest.core.IsNull;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.newatlanta.commons.vfs.provider.gae.GaeClassLoader;
import com.newatlanta.commons.vfs.provider.gae.GaeFileSystemManager;
import com.newatlanta.commons.vfs.provider.gae.GaeVFS;
 
/**
 * @author Aslak Helles'y
 * @version $Revision: 2502 $
 */
public class GaeClassloadingTestCase  {

	
    private String jarsDir = "src/test/resources";
 
    GaeFileSystemManager manager = null;

    private final LocalServiceTestHelper helper =
        new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());

    @BeforeClass
    public static void init() {
        GaeVFS.setRootPath( new java.io.File("./target").getAbsolutePath() );    	
    }
    
    @Before
    public void setUp() throws Exception  {
        helper.setUp();
        manager = GaeVFS.getManager();
        
        //manager.addProvider("zip", new ZipFileProvider());
    	//manager.init();
    }

    @After
    public void tearDown() {
        helper.tearDown();
    }


    @Test @Ignore
    public void uncompressJar() throws Exception {
    
    	
    	FileInputStream fis = new FileInputStream( "src/test/resources/ftp4j-1.4.2.jar" );
    	
    	JarInputStream jis = new JarInputStream( fis );
    	
		JarEntry entry;
		java.io.File target = new java.io.File("target");
		
		while ((entry = jis.getNextJarEntry()) != null) {
			
			
			if (entry.isDirectory() ) {
				
				java.io.File folder = new java.io.File( target, entry.getName() );
				folder.mkdirs();
				continue;
			}
			
			System.out.println( entry ) ;
			
	        long sz = entry.getSize();
	        
	        byte[] buffer;
	        int bufferLen = 0;
	        if( sz > 0 ) {
		        buffer = new byte[ (int)sz ];

		        int avail ;
		        while( (avail = jis.available()) > 0 && bufferLen < sz ) {
		        	bufferLen += jis.read(buffer, bufferLen, avail);
		        }
		        
				System.out.printf( String.format("read entry [%s]  size [%d] red [%d]\n", entry, sz, bufferLen));
				
				
				FileOutputStream fos = new FileOutputStream( new java.io.File( target, entry.getName()));
				
				fos.write( buffer, 0,bufferLen);
				
				fos.close();
	        }
			
			jis.closeEntry();
		}
		
		jis.close();
		fis.close();
		
    }
    
    @Test //@Ignore
    public void loadClassFromVFS() throws Exception {
  	
    	FileObject root = getRootFolder( "mylib");
    	
    	FileObject jar1 =  root.resolveFile( "json.jar");

    	Assert.assertThat(jar1, notNullValue());
        System.out.printf( "class [%s]\n", jar1.getClass().getName());
        Assert.assertThat(jar1, instanceOf(com.newatlanta.commons.vfs.provider.gae.GaeFileObject.class));
        
        getFileObjectFromArchive( jar1, "src/test/resources/json-20080701.jar");
        
    	FileObject jar2 = root.resolveFile( "ftp4j-1.4.2.jar");
    	
        Assert.assertThat(jar2, notNullValue());
        System.out.printf( "class [%s]\n", jar2.getClass().getName());
        Assert.assertThat(jar2, instanceOf(com.newatlanta.commons.vfs.provider.gae.GaeFileObject.class));

        getFileObjectFromArchive( jar2, "src/test/resources/ftp4j-1.4.2.jar");
        
        final FileObject[] children = root.getChildren();
        
        final GaeClassLoader cl = new GaeClassLoader( children, manager, getClass().getClassLoader());
    	
        //final String className = "org.json.JSONObject";
        final String className = "it.sauronsoftware.ftp4j.FTPClient";
        
        Class<?> c = cl.loadClass(className);
        
        Assert.assertThat(c, notNullValue());
        Assert.assertThat( c.getName(), equalTo(className));
        /*
        Enumeration<URL> e = cl.getResources("META-INF/MANIFEST.MF");
        
        Assert.assertThat( e, notNullValue());        
        Assert.assertThat( e.hasMoreElements(), Is.is(true));
        
        boolean foundIt = false;
        while( e.hasMoreElements() ) {
        	
        	final URL url = e.nextElement();
        	System.out.printf( "META-INF/MANIFEST.MF [%s]\n", url);
        	
        	if( url.toString().toLowerCase().contains("ftp4j-1.4.2.jar")) {
        		foundIt = true;
        		break;
        	}
        }
        Assert.assertThat( foundIt, Is.is(true));
        
        Enumeration<URL> e1 = cl.getResources("META-INF/MANIFEST.MF");
        
        java.io.InputStream is = e1.nextElement().openStream();
        
        Assert.assertThat( is, notNullValue());        
        Assert.assertThat( is.available() > 0, Is.is(true));
        */
     }
    
    @Test @Ignore
    public void testZipWithDeploymentScriptAndClassesCanBeDeployed() throws Exception {
       
    	FileObject applicationArchive = getFileObjectFromArchive(manager, jarsDir.concat("/json.jar"));
                
        final GaeClassLoader cl = new GaeClassLoader(applicationArchive, manager, getClass().getClassLoader());

        ContextFactory.getGlobal().initApplicationClassLoader(cl);
        
        Assert.assertThat(cl, notNullValue());
        
        {
	        final String className = "org.json.JSONObject";
	        
	        Class<?> c = cl.loadClass(className);
	        
	        Assert.assertThat(c, notNullValue());
	        Assert.assertThat( c.getName(), equalTo(className));
	        
	        Object o = c.newInstance();
	        
	        Assert.assertThat(o, notNullValue());
	        Assert.assertThat( o.getClass().getName(), equalTo(className));
	        
	        System.out.printf( "object [%s]\n", o);
        }
 
        {
	        Context ctx = Context.enter();
	        
	        try {
	            final String source = new StringBuilder()
	  			.append( "var jsonpkg = Packages.org.json;")
	  			.append( "var o = new jsonpkg.JSONObject();")
	  			.append( "out.println( 'JSONObject = ' + o );")
	  			.toString();
	        	
	            final Scriptable scope = new ImporterTopLevel(ctx);
	        	
	        	ScriptableObject.putProperty(scope, "out", System.out);
	        	ctx.evaluateString(scope, source, "test", 1, null);
	        	
	        }
	        finally {
	        	Context.exit();
	        }
        }
        
    	FileObject applicationArchive2 = getFileObjectFromArchive(manager, "/ftp4j-1.4.2.jar");
        
        cl.addFileObjects( manager, new FileObject[]{ applicationArchive2 });

        {
            final String className = "it.sauronsoftware.ftp4j.FTPClient";
            
            Class<?> c = cl.loadClass(className);
            
            Assert.assertThat(c, notNullValue());
            Assert.assertThat( c.getName(), equalTo(className));
            
            Object o = c.newInstance();
            
            Assert.assertThat(o, notNullValue());
            Assert.assertThat( o.getClass().getName(), equalTo(className));
            
            System.out.printf( "object [%s]\n", o);
        }
/*
        ScriptEngineManager mgr = new ScriptEngineManager( cl );
        
        ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
        try {
        	
          	
          final String script = new StringBuilder()
          			.append( "println('Hello, world!');" )
          			//.append( "var FTPClient = Packages.it.sauronsoftware.ftp4j.FTPClient;")
          			//.append( "var fp = new FTPClient();")
          			.append( "var JSONObject = Packages.org.json.JSONObject;")
          			.append( "var fp = new JSONObject();")
          			.toString();
          
          jsEngine.eval( script );
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } 
*/
        {
	        Context ctx = Context.enter();
	        
	        try {
	            final String source = new StringBuilder()
	  			.append( "var ftppkg = Packages.it.sauronsoftware.ftp4j;")
	  			.append( "var f = new ftppkg.FTPClient();")
	  			.append( "out.println( 'FTPClient = ' + f );")
	  			.append( "var jsonpkg = Packages.org.json;")
	  			.append( "var o = new jsonpkg.JSONObject();")
	  			.append( "out.println( 'JSONObject = ' + o );")
	  			.toString();
	        	
	        	Scriptable scope = new ImporterTopLevel(ctx);
	        	
	        	ScriptableObject.putProperty(scope, "out", System.out);
	        	ctx.evaluateString(scope, source, "test", 1, null);
	        	
	        }
	        finally {
	        	Context.exit();
	        }
        }
        
        
        
    }
 
    @Test @Ignore
    public void testBrowseFilesOnVFS() throws Exception {
       
    	FileObject root = getRootFolder( "gae://lib" );
    	
    	Assert.assertThat( root,  IsNull.notNullValue());
    	
    	{
    	final String path = jarsDir + "/json.jar";
    	
    	//FileObject file = getApplicationArchive( manager.resolveFile( root, "json-1.0.jar"),  path );
    	FileObject file = getFileObjectFromArchive( root.resolveFile( "json-1.0.jar"),  path );

    	Assert.assertThat( file,  IsNull.notNullValue());
    	
    	}

    	{
    	final String path = jarsDir + "/ftp4j-1.4.2.jar";
    	
    	FileObject file = getFileObjectFromArchive( manager.resolveFile( root, "ftp4j-1.4.2.jar"),  path );

    	Assert.assertThat( file,  IsNull.notNullValue());
    	
    	}

    	final FileObject[] children = root.getChildren();
    	
    	Assert.assertThat( children,  IsNull.notNullValue());
    	Assert.assertThat( children.length,  IsEqual.equalTo(2));
        
        for( FileObject fo : children ) {
        	
        	System.out.printf( "fo [%s]:[%s]\n", fo.getName().getBaseName(), fo.getName().getPath() );
        }
        
        final GaeClassLoader cl = new GaeClassLoader(children, manager, getClass().getClassLoader());
    	Assert.assertThat( cl,  IsNull.notNullValue());

    }
    
    private FileObject getRootFolder( String path ) throws FileSystemException {

    	FileObject result = manager.resolveFile( "gae://".concat(path) );

    	Assert.assertThat(result,  IsNull.notNullValue());
    	
    	if( !result.exists() ) {
    		
    		result.createFolder();
    	}
    	
    	Assert.assertThat(result.getType(),  IsEqual.equalTo(FileType.FOLDER));
    	    	
    	return result;
    }
    
    private FileObject getFileObjectFromArchive(final FileObject file, final String jarPath) throws Exception {

    	if( !file.exists() ) {

    		File src = new File(jarPath);
    		FileInputStream fis = new FileInputStream(src);
    	
    		byte buff[] = new byte[ (int) src.length() ];
        
    		fis.read(buff);
        
    		file.createFile();
    		
    		file.getContent().getOutputStream().write(buff);
    		
    		file.getContent().close();
    		
    		fis.close();
    	}
    	return file;
    }
    
    private FileObject getFileObjectFromArchive(final DefaultFileSystemManager manager, final String targetPath, final String jarPath) throws Exception {

    	return getFileObjectFromArchive( manager.resolveFile("gae://".concat(targetPath) ), jarPath );
    }

    private FileObject getFileObjectFromArchive(final DefaultFileSystemManager manager, final String jarPath) throws Exception {

    	return getFileObjectFromArchive( manager, jarPath, jarPath);
    }
 
}